feat(document): split, merge and insert a paragraph - #874
Merged
Conversation
andiwand
force-pushed
the
feat/text-edit-runs
branch
from
September 10, 2026 12:56
0fb6032 to
315dcd7
Compare
andiwand
force-pushed
the
feat/text-edit-paragraphs
branch
from
September 10, 2026 12:56
4210894 to
5e1f351
Compare
andiwand
force-pushed
the
feat/text-edit-runs
branch
4 times, most recently
from
September 10, 2026 13:27
d0a4632 to
07199f0
Compare
andiwand
force-pushed
the
feat/text-edit-paragraphs
branch
from
September 10, 2026 13:33
5e1f351 to
267399e
Compare
`splitParagraph` moves what follows the element it names into a new paragraph carrying the same style, `mergeParagraph` takes the children of the paragraph after this one and removes it, and `insertParagraph` puts an empty one after it. With the run operations, Enter, Backspace at the start of a paragraph and a delete spanning paragraphs are all replayable. `Document::split_paragraph`, `merge_paragraphs` and `insert_paragraph_after` are the same three in the C++ API, on the document rather than on a handle for the reason the run operations are. A split names a descendant rather than a direct child, because the caret sits in a run and the run sits in a span. So it splits every element from that run up to the paragraph: a run inside a span leaves the span in both halves, and the tail keeps the formatting it had. Only a span and a link are split through; anything else refuses, because what a copy of it would mean is the format's question rather than this one's. A copy carries the original's attributes and the property children the format writes ahead of the content - `w:pPr`, `w:rPr`. Those sit before the first child the registry knows, which is how the copy finds them without naming a tag. Verified with headless LibreOffice: it reads the split paragraph out of both a saved `.odt` and a saved `.docx` as two paragraphs, the second keeping the style of the first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKFKbUVCYF2VhujdmjhhPW
andiwand
force-pushed
the
feat/text-edit-paragraphs
branch
from
September 10, 2026 13:44
267399e to
7c538eb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Generated with Claude Code
Third of five; #873 is merged, so this now sits on main. See
docs/design/document-editing.md.What this one does
splitParagraphmoves what follows the element it names into a new paragraphcarrying the same style.
mergeParagraphtakes the children of the paragraph after this one andremoves it.
insertParagraphputs an empty one after it.With the run operations, Enter, Backspace at the start of a paragraph
and a delete spanning paragraphs are all replayable.
Document::split_paragraph,merge_paragraphsandinsert_paragraph_afterare the same three in the C++ API — on the document rather than on a handle,
for the reason the run operations are: a handle says what an element holds,
the document what the tree holds. Each refuses an element of another document.
A split names a descendant, not a direct child
The caret sits in a run and the run sits in a span, so the split walks from
that run up to the paragraph and splits every element on the way: a run
inside a span leaves the span in both halves, and the tail keeps the formatting
it had. The same holds for a link, so the tail is still a link to the same
place.
Only a span and a link are split through. Anything else — a frame
between the run and the paragraph — refuses with
UnsupportedOperation,because what a copy of it would mean is the format's question rather than this
one's.
A copy carries the original's attributes and the property children the format
writes ahead of the content —
w:pPron a paragraph,w:rPron a run. Thosesit before the first child the registry knows about, which is how the copy
finds them without naming a tag. ODF states the same thing as an attribute, so
one rule covers both.
Verified
Headless LibreOffice reads the split paragraph out of both a saved
.odtand asaved
.docxas two paragraphs, the second keeping the style of the first.